#ifndef __MY_WEB_SERVER_H__
#define __MY_WEB_SERVER_H__

#include <cstdio>
#include <string>

#include <freertos/FreeRTOS.h>
#include <esp_event.h>
#include <esp_http_server.h>
#include <map>

#ifdef __cplusplus

class WebServer
{
private:
    int port;
    int max_uri_handlers;
    httpd_handle_t server;


    
    /**
     * @brief Removes invisible characters from a string
     *
     * @param str The string from which to remove invisible characters
     */
    void removeInvisibleChars(std::string &str);



public:
    /**
     * Constructor for the WebServer class, which initializes the port and maximum number of URI handlers
     *
     * @param port The port on which the server will listen
     * @param max_uri_handlers The maximum number of URI handlers that can be registered
     */
    WebServer(int port = 80, int max_uri_handlers = 5);

    /**
     * Starts the web server
     *
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t start();

    /**
     * Stops the web server
     *
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t stop();

    /**
     * Registers a URI handler
     *
     * @param uri The URI to be handled
     * @param method The HTTP method to be handled
     * @param handleFunction The function to be called when the URI is requested
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t registUri(const char *uri, httpd_method_t method, esp_err_t (*handleFunction)(httpd_req_t *r), void *user_arg = nullptr);

    /**
     * Registers a handler for requests that do not match any registered URI
     *
     * @param method The HTTP method to be handled
     * @param handleFunction The function to be called when the URI is requested
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t registNotFound(httpd_method_t method, esp_err_t (*handleFunction)(httpd_req_t *r), void *user_arg = nullptr);

    /**
     * Sets the handler for the web server
     *
     * @param r The HTTP request object
     * @param field The field to be set
     * @param value The value to be set
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t setHeader(httpd_req_t *r, const char *field, const char *value);

    /**
     * Sends a string response to a client request
     *
     * @param req The HTTP request object
     * @param senString The string to be sent as the response
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t send(httpd_req_t *req, const char *senString);
    esp_err_t send(httpd_req_t *req, const std::string &senString);

    /**
     * Sends a file response to a client request
     *
     * @param req The HTTP request object
     * @param file The file to be sent as the response
     * @param file_type The MIME type of the file
     * @return An esp_err_t value indicating the success or failure of the operation
     */
    esp_err_t send(httpd_req_t *req, FILE *file, const std::string &file_type);

    /**
     * Gets the URI of a request
     *
     * @param req The HTTP request object
     * @return A string containing the URI of the request
     */
    std::string uri(httpd_req_t *req);

    /**
     * Gets the value of a query parameter from a request
     *
     * @param req The HTTP request object
     * @param key The name of the query parameter
     * @return A string containing the value of the query parameter
     */
    std::string arg(httpd_req_t *req, std::string key);
    /**
     * @brief Parses form data from a string
     *
     * @param data The string containing the form data
     * @return A map of key-value pairs parsed from the form data
     */
    std::map<std::string, std::string> parseFormData(std::string &data);

    std::string getBody(httpd_req_t *req);
};

class DNSServer
{
private:
    static int create_udp_socket(int port);
    bool avaliable = true;
    uint8_t ip[4] = {192, 168, 4, 1};
    int sock = -1;

    

public:
    DNSServer(uint8_t a = 192, uint8_t b = 168, uint8_t c = 4, uint8_t d = 1)
    {
        ip[0] = a;
        ip[1] = b;
        ip[2] = c;
        ip[3] = d;
    }
    void init();
    void poll();
    void stop();
};

/**
 * Get the content (MIME) type of a file based on its extension
 *
 * @param filename The name of the file
 * @return The MIME type of the file
 */
std::string getContentType(std::string filename);

/**
 * Check if a string ends with a specific substring
 *
 * @param str The string to check
 * @param target The substring to check for
 * @return true if the string ends with the substring, false otherwise
 */
bool endsWith(const std::string &str, const std::string &target);

#endif
#endif